package net.luohuasheng.bee.proxy.mybatis;

import net.luohuasheng.bee.proxy.mybatis.common.enums.BasicMethod;
import net.luohuasheng.bee.proxy.mybatis.common.utils.CrudUtils;
import net.luohuasheng.bee.proxy.mybatis.dao.BasicReadDao;
import net.luohuasheng.bee.proxy.mybatis.executor.mapper.CrudMapper;
import net.luohuasheng.bee.proxy.mybatis.executor.processor.BaseProcessor;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.Configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

/**
 * @author panda
 */
public class ConfigurationProxy extends Configuration {
    public static final String END_FLAG = "$$crud";
    private Logger logger = LoggerFactory.getLogger(getClass());
    private Map<Class<?>, CrudMapper> crudMapperMap = new HashMap<>(0);
    private static final ThreadLocal<BasicMethod> BASIC_METHOD_THREAD_LOCAL = new ThreadLocal<>();

    @Override
    public boolean hasStatement(String statementName) {
        boolean hasStatement = super.hasStatement(statementName);
        if (!hasStatement) {
            String name = statementName.substring(statementName.lastIndexOf(".") + 1);
            BasicMethod method = BasicMethod.getMethodByCode(name);
            if (method != null) {
                BASIC_METHOD_THREAD_LOCAL.set(method);
                return true;
            } else {
                return false;
            }
        } else {
            return true;
        }
    }


    @Override
    public MappedStatement getMappedStatement(String id) {
        try {
            return super.getMappedStatement(id);
        } catch (Exception e) {
            BasicMethod method = BASIC_METHOD_THREAD_LOCAL.get();
            if (method != null) {
                String className = id.substring(0, id.lastIndexOf("."));
                try {
                    Class<?> classZ = this.getClass().getClassLoader().loadClass(className);
                    Type[] genericInterfaces = classZ.getGenericInterfaces();
                    ParameterizedType genericInterface = (ParameterizedType) genericInterfaces[0];
                    if (BasicReadDao.class.isAssignableFrom((Class<?>) genericInterface.getRawType())) {
                        CrudMapper crudMapper = crudMapperMap.computeIfAbsent(classZ, v -> {
                            MapperBuilderAssistant assistant = new MapperBuilderAssistant(this, classZ.getName().replaceAll("\\.", "/") + END_FLAG);
                            assistant.setCurrentNamespace(classZ.getName());
                            return new CrudMapper(assistant, this);
                        });
                        Class<?> entityClass = (Class<?>) genericInterface.getActualTypeArguments()[0];
                        BaseProcessor processor = CrudUtils.getBaseProcessor(this, id);
                        if (processor == null) {
                            return null;
                        }
                        switch (method.getSqlCommandType()) {
                            case UNKNOWN:
                            case FLUSH:
                                return null;
                            case INSERT:
                                return crudMapper.addMappedStatement(id, processor.createSqlSource(), method.getSqlCommandType(), Object.class, Object.class, processor.keyGenerator(), processor.keyProperty(), processor.keyColumn());
                            case UPDATE:
                            case DELETE:
                                return crudMapper.addMappedStatement(id, processor.createSqlSource(), method.getSqlCommandType(), Object.class, Object.class, new NoKeyGenerator(), null, null);
                            case SELECT:
                                if (method.getCode().startsWith(BasicMethod.METHOD_GET_VALUE.getCode()) || method.equals(BasicMethod.METHOD_GET_COUNT)) {
                                    return crudMapper.addMappedStatement(id, processor.createSqlSource(), method.getSqlCommandType(), Object.class, Object.class, null, null, null);
                                } else {
                                    return crudMapper.addMappedStatement(id, processor.createSqlSource(), method.getSqlCommandType(), Object.class, entityClass, null, null, null);
                                }
                            default:
                                return null;

                        }
                     }
                } catch (ClassNotFoundException ex) {
                    ex.printStackTrace();
                }
            }
            return null;
        } finally {
            BASIC_METHOD_THREAD_LOCAL.remove();
        }


    }


}
